home *** CD-ROM | disk | FTP | other *** search
/ Aminet 34 / Aminet 34 (2000)(Schatztruhe)[!][Dec 1999].iso / Aminet / util / gnu / unixcmds.lha / unixcmds / src / grep.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-10-06  |  10.3 KB  |  332 lines

  1. /* grep - search a file for a pattern   Author: Norbert Schlenker */
  2.  
  3. /* Norbert Schlenker (nfs@princeton.edu)  1990-02-08
  4.  * Released into the public domain.
  5.  *
  6.  * Grep searches files for lines containing a pattern, as specified by
  7.  * a regular expression, and prints those lines.  It is invoked by:
  8.  *      grep [flags] [pattern] [file ...]
  9.  *
  10.  * Flags:
  11.  *      -e pattern      useful when pattern begins with '-'
  12.  *      -c              print a count of lines matched
  13.  *      -i              ignore case
  14.  *      -l              prints just file names, no lines (quietly overrides -n)
  15.  *      -n              printed lines are preceded by relative line numbers
  16.  *      -s              prints errors only (quietly overrides -l and -n)
  17.  *      -v              prints lines which don't contain the pattern
  18.  *
  19.  * Semantic note:
  20.  *      If both -l and -v are specified, grep prints the names of those
  21.  *      files which do not contain the pattern *anywhere*.
  22.  *
  23.  * Exit:
  24.  *      Grep sets an exit status which can be tested by the caller.
  25.  *      Note that these settings are not necessarily compatible with
  26.  *      any other version of grep, especially when -v is specified.
  27.  *      Possible status values are:
  28.  *        0     if any matches are found
  29.  *        1     if no matches are found
  30.  *        2     if syntax errors are detected or any file cannot be opened
  31.  */
  32.  
  33.  
  34. /* External interfaces */
  35. #include <sys/types.h>
  36. #include "regex.h"             /* Thanks to Henry Spencer */
  37. #include <stdlib.h>
  38. #include <string.h>
  39. #include <stdio.h>
  40. #include <unistd.h>
  41.  
  42. /* Internal constants */
  43. #define MATCH           0       /* exit code: some match somewhere */
  44. #define NO_MATCH        1       /* exit code: no match on any line */
  45. #define FAILURE         2       /* exit code: syntax error or bad file name */
  46.  
  47. /* Macros */
  48. #define SET_FLAG(c)     (flags[(c)-'a'] = 1)
  49. /* [<][>][^][v][top][bottom][index][help] */
  50. #define FLAG(c)         (flags[(c)-'a'] != 0)
  51. /* [<][>][^][v][top][bottom][index][help] */
  52.  
  53. #define uppercase(c)    (((unsigned) ((c) - 'A')) <= ('Z' - 'A'))
  54. /* [<][>][^][v][top][bottom][index][help] */
  55. #define downcase(c)     ((c) - 'A' + 'a')
  56. /* [<][>][^][v][top][bottom][index][help] */
  57.  
  58. /* Private storage */
  59. static char *program;           /* program name */
  60. static char flags[26];          /* invocation flags */
  61. static regexp *expression;      /* compiled search pattern */
  62.  
  63. /* External variables. */
  64. extern int optind;
  65. extern char *optarg;
  66.  
  67. /* Internal interfaces */
  68. int main  (int argc, char **argv);
  69. static int match  (FILE *input, char *label, char *filename);
  70. static char *get_line  (FILE *input);
  71. static char *map_nocase  (char *line);
  72. static void error_exit  (char *msg);
  73. void regerror   (char *s ) ;
  74.  
  75.  
  76. int main(argc, argv)
  77. /* [<][>][^][v][top][bottom][index][help] */
  78. int argc;
  79. char *argv[];
  80. {
  81.   int opt;                      /* option letter from getopt() */
  82.   char *pattern;                /* search pattern */
  83.   int exit_status = NO_MATCH;   /* exit status for our caller */
  84.   int file_status;              /* status of search in one file */
  85.   FILE *input;                  /* input file (if not stdin) */
  86.  
  87.   program = argv[0];
  88.   memset(flags, 0, sizeof(flags));
  89.   pattern = NULL;
  90.  
  91. /* Process any command line flags. */
  92.   while ((opt = getopt(argc, argv, "e:cilnsv")) != EOF) {
  93.         if (opt == '?')
  94.                 exit_status = FAILURE;
  95.         else
  96.         if (opt == 'e')
  97.                 pattern = optarg;
  98.         else
  99.                 SET_FLAG(opt);
  100.   }
  101.  
  102. /* Detect a few problems. */
  103.   if ((exit_status == FAILURE) || (optind == argc && pattern == NULL))
  104.         error_exit("Usage: %s [-cilnsv] [-e] expression [file ...]\n");
  105.  
  106. /* Ensure we have a usable pattern. */
  107.   if (pattern == NULL)
  108.         pattern = argv[optind++];
  109.  
  110. /* Map pattern to lowercase if -i given. */
  111.   if (FLAG('i')) {
  112.         char *p;
  113.         for (p = pattern; *p != '\0'; p++) {
  114.                 if (uppercase(*p))
  115.                         *p = downcase(*p);
  116.         }
  117.   }
  118.  
  119.   if ((expression = regcomp(pattern)) == NULL)
  120.         error_exit("%s: bad regular expression\n");
  121.  
  122. /* Process the files appropriately. */
  123.   if (optind == argc) {         /* no file names - find pattern in stdin */
  124.         exit_status = match(stdin, (char *) NULL, "<stdin>");
  125.   }
  126.   else 
  127.   if (optind + 1 == argc) {     /* one file name - find pattern in it */
  128.         if (strcmp(argv[optind], "-") == 0) {
  129.                 exit_status = match(stdin, (char *) NULL, "-");
  130.         } else {
  131.                 if ((input = fopen(argv[optind], "r")) == NULL) {
  132.                         fprintf(stderr, "%s: couldn't open %s\n",
  133.                                                         program, argv[optind]);
  134.                         exit_status = FAILURE;
  135.                 }
  136.                 else {
  137.                         exit_status = match(input, (char *) NULL, argv[optind]);
  138.                 }
  139.         }
  140.   }
  141.   else
  142.   while (optind < argc) {       /* lots of file names - find pattern in all */
  143.         if (strcmp(argv[optind], "-") == 0) {
  144.                 file_status = match(stdin, "-", "-");
  145.         } else {
  146.                 if ((input = fopen(argv[optind], "r")) == NULL) {
  147.                         fprintf(stderr, "%s: couldn't open %s\n",
  148.                                                         program, argv[optind]);
  149.                         exit_status = FAILURE;
  150.                 } else {
  151.                         file_status = match(input, argv[optind], argv[optind]);
  152.                         fclose(input);
  153.                 }
  154.         }
  155.         if (exit_status != FAILURE)
  156.                 exit_status &= file_status;
  157.         ++optind;
  158.   }
  159.   return(exit_status);
  160. }
  161.  
  162.  
  163. /* match - matches the lines of a file with the regular expression.
  164.  * To improve performance when either -s or -l is specified, this
  165.  * function handles those cases specially.
  166.  */
  167.  
  168. static int match(input, label, filename)
  169. /* [<][>][^][v][top][bottom][index][help] */
  170. FILE *input;
  171. char *label;
  172. char *filename;
  173. {
  174.   char *line, *testline;        /* pointers to input line */
  175.   long int lineno = 0;          /* line number */
  176.   long int matchcount = 0;      /* lines matched */
  177.   int status = NO_MATCH;        /* summary of what was found in this file */
  178.  
  179.   if (FLAG('s') || FLAG('l')) {
  180.         while ((line = get_line(input)) != NULL) {
  181.                 testline = FLAG('i') ? map_nocase(line) : line;
  182.                 if (regexec(expression, testline, 1)) {
  183.                         status = MATCH;
  184.                         break;
  185.                 }
  186.         }
  187.         if (FLAG('l'))
  188.                 if ((!FLAG('v') && status == MATCH) ||
  189.                     ( FLAG('v') && status == NO_MATCH))
  190.                         puts(filename);
  191.         return status;
  192.   }
  193.  
  194.   while ((line = get_line(input)) != NULL) {
  195.         ++lineno;
  196.         testline = FLAG('i') ? map_nocase(line) : line;
  197.         if (regexec(expression, testline, 1)) {
  198.                 status = MATCH;
  199.                 if (!FLAG('v')) {
  200.                         if (label != NULL)
  201.                                 printf("%s:", label);
  202.                         if (FLAG('n'))
  203.                                 printf("%ld:", lineno);
  204.                         if (!FLAG('c')) puts(line);
  205.                         matchcount++;
  206.                 }
  207.         } else {
  208.                 if (FLAG('v')) {
  209.                         if (label != NULL)
  210.                                 printf("%s:", label);
  211.                         if (FLAG('n'))
  212.                                 printf("%ld:", lineno);
  213.                         if (!FLAG('c')) puts(line);
  214.                         matchcount++;
  215.                 }
  216.         }
  217.   }
  218.   if (FLAG('c')) printf("%ld\n", matchcount);
  219.   return status;
  220. }
  221.  
  222.  
  223. /* get_line - fetch a line from the input file
  224.  * This function reads a line from the input file into a dynamically
  225.  * allocated buffer.  If the line is too long for the current buffer,
  226.  * attempts will be made to increase its size to accomodate the line.
  227.  * The trailing newline is stripped before returning to the caller.
  228.  */
  229.  
  230. #define FIRST_BUFFER (size_t)256                /* first buffer size */
  231.  
  232. static char *buf = NULL;        /* input buffer */
  233. static size_t buf_size = 0;             /* input buffer size */
  234.  
  235. static char *get_line(input)
  236. /* [<][>][^][v][top][bottom][index][help] */
  237. FILE *input;
  238. {
  239.   int n;
  240.   register char *bp;
  241.   register int c;
  242.   char *new_buf;
  243.   size_t new_size;
  244.  
  245.   if (buf_size == 0) {
  246.         if ((buf = (char *) malloc(FIRST_BUFFER)) == NULL)
  247.                 error_exit("%s: not enough memory\n");
  248.         buf_size = FIRST_BUFFER;
  249.   }
  250.  
  251.   bp = buf;
  252.   n = buf_size;
  253.   while (1) {
  254.         while (--n > 0 && (c = getc(input)) != EOF) {
  255.                 if (c == '\n') {
  256.                         *bp = '\0';
  257.                         return buf;
  258.                 }
  259.                 *bp++ = c;
  260.         }
  261.         if (c == EOF)
  262.                 return (ferror(input) || bp == buf) ? NULL : buf;
  263.         new_size = buf_size << 1;
  264.         if ((new_buf = (char *) realloc(buf, new_size)) == NULL) {
  265.                 fprintf(stderr, "%s: line too long - truncated\n", program);
  266.                 while ((c = getc(input)) != EOF && c != '\n') ;
  267.                 *bp = '\0';
  268.                 return buf;
  269.         } else {
  270.                 bp = new_buf + (buf_size - 1);
  271.                 n = buf_size + 1;
  272.                 buf = new_buf;
  273.                 buf_size = new_size;
  274.         }
  275.   }
  276. }
  277.  
  278.  
  279. /* map_nocase - map a line down to lowercase letters only.
  280.  * bad points:  assumes line gotten from get_line.
  281.  *              there is more than A-Z you say?
  282.  */
  283.  
  284. static char *map_nocase(line)
  285. /* [<][>][^][v][top][bottom][index][help] */
  286. char *line;
  287. {
  288.   static char *mapped;
  289.   static size_t map_size = 0;
  290.   char *mp;
  291.  
  292.   if (map_size < buf_size) {
  293.         if (map_size == 0) {
  294.                 mapped = (char *) malloc(buf_size);
  295.         } else {
  296.                 mapped = (char *) realloc(mapped, buf_size);
  297.         }
  298.         if (mapped == NULL)
  299.                 error_exit("%s: not enough memory\n");
  300.         map_size = buf_size;
  301.   }
  302.  
  303.   mp = mapped;
  304.   do {
  305.         *mp++ = uppercase(*line) ? downcase(*line) : *line;
  306.   } while (*line++ != '\0');
  307.  
  308.   return mapped;
  309. }
  310.  
  311.  
  312. /* Regular expression code calls this routine to print errors. */
  313.  
  314. void regerror(s)
  315. /* [<][>][^][v][top][bottom][index][help] */
  316. char *s;
  317. {
  318.   fprintf(stderr, "regexp: %s\n", s);
  319. }
  320.  
  321.  
  322. /* Common exit point for outrageous errors. */
  323.  
  324. static void error_exit(msg)
  325. /* [<][>][^][v][top][bottom][index][help] */
  326. char *msg;
  327. {
  328.   fprintf(stderr, msg, program);
  329.   exit(FAILURE);
  330. }
  331. /* [<][>][^][v][top][bottom][index][help] */
  332.